package com.todo.strategy.offline.application.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.todo.common.common.constants.BizConstants;
import com.todo.common.common.entity.response.BizResponse;
import com.todo.common.common.enums.BizErrorCodeEnum;
import com.todo.common.common.exception.BizException;
import com.todo.common.utils.TodoCommonUtils;
import com.todo.component.redis.RedisService;
import com.todo.strategy.common.constant.StrategyOfflineKeyBuild;
import com.todo.strategy.common.enums.PlanRelationSceneEnum;
import com.todo.strategy.common.enums.StrategyRelationTypeEnum;
import com.todo.strategy.offline.application.convert.StrategyConvert;
import com.todo.strategy.offline.application.entity.bo.PlanRelationBO;
import com.todo.strategy.offline.application.entity.context.RelationSimpleContext;
import com.todo.strategy.offline.application.handler.relation.StrategyRelationFactory;
import com.todo.strategy.offline.application.handler.relation.StrategyRelationStrategy;
import com.todo.strategy.offline.application.remote.StrategyPipelineRemoteService;
import com.todo.strategy.offline.application.service.PlanRelationService;
import com.todo.strategy.offline.infrastructure.entity.po.PlanRelationPO;
import com.todo.strategy.offline.infrastructure.repository.PlanRelationRepository;
import com.todo.strategy.pipeline.common.enums.OperateActionEnum;
import com.todo.strategy.pipeline.common.enums.StrategyActionEnum;
import com.todo.strategy.pipeline.service.api.request.OfflineToOnlineRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * (PlanRelation)表服务实现类
 *
 * @author zhangwenxiang
 * @since 2024-03-04 00:19:47
 */
@Slf4j
@Service("planRelationService")
public class PlanRelationServiceImpl implements PlanRelationService {

    @Resource
    private PlanRelationRepository planRelationRepository;
    @Resource
    private StrategyRelationFactory strategyRelationFactory;
    @Resource
    private RedisService redisService;
    @Resource
    private StrategyPipelineRemoteService strategyPipelineRemoteService;

    @Override
    public Boolean insertPlanRelation(PlanRelationBO planRelationBO) {
        if (planRelationBO == null) {
            throw new BizException(BizErrorCodeEnum.PARAM_ERROR);
        }
        if (planRelationBO.getBusinessId() == null || planRelationBO.getBusinessScene() == null) {
            throw new BizException(BizErrorCodeEnum.PARAM_ERROR, "核心数据为空");
        }
        List<PlanRelationBO> planRelationBOList = queryPlanRelationByBizId(planRelationBO.getBusinessId(), planRelationBO.getBusinessScene());
        if (CollectionUtils.isNotEmpty(planRelationBOList)) {
            if (planRelationBOList.size() > BizConstants.DEFAULT_MAX_PLAN_RELATION_SIZE) {
                log.warn("PlanRelationService#insertPlanRelation 新增计划关联信息失败，当前业务关联下数据超限，默认不允许操作 planRelationBO：【{}】", JSONObject.toJSONString(planRelationBO));
                throw new BizException(BizErrorCodeEnum.RELATION_DATA_EXCESSIVE);
            }

            //判断历史数据是否存在某些信息一致
            for (PlanRelationBO item : planRelationBOList) {
                if (item.getTitle().equals(planRelationBO.getTitle()) && item.getPlanDay().equals(planRelationBO.getPlanDay())) {
                    log.warn("PlanRelationService#insertPlanRelation 新增计划关联信息失败，当前业务关联下标题名称重复，默认不允许操作 planRelationBO：【{}】", JSONObject.toJSONString(planRelationBO));
                    throw new BizException(BizErrorCodeEnum.PARAM_ERROR, "标题名称重复");
                }
                if (item.getStartTime().equals(planRelationBO.getStartTime()) && item.getPlanDay().equals(planRelationBO.getPlanDay())) {
                    log.warn("PlanRelationService#insertPlanRelation 新增计划关联信息失败，当前业务关联下开始时间重复，默认不允许操作 planRelationBO：【{}】", JSONObject.toJSONString(planRelationBO));
                    throw new BizException(BizErrorCodeEnum.PARAM_ERROR, "开始时间重复");
                }
            }
        }

        //非自定义类型枚举，则通过查询填充对应数据
        if (!StrategyRelationTypeEnum.CUSTOM.equals(planRelationBO.getRelationType())) {
            StrategyRelationStrategy strategy = strategyRelationFactory.getStrategy(planRelationBO.getRelationType());
            if (strategy == null) {
                log.error("PlanRelationService#insertPlanRelation 新增计划关联信息失败，数据关联类型错误，无当前数据处理器 无法操作 planRelationBO：【{}】", JSONObject.toJSONString(planRelationBO));
                throw new BizException(BizErrorCodeEnum.PARAM_ERROR, "关联信息出错，请检查后操作");
            }
            RelationSimpleContext context = strategy.relationSimpleHandel(planRelationBO.getRelationId());
            if (context == null) {
                log.error("PlanRelationService#insertPlanRelation 新增计划关联信息失败，数据关联类型查询为空 默认保存空数据 planRelationBO：【{}】", JSONObject.toJSONString(planRelationBO));
            } else {
                PlanRelationBO.Extra extra = new PlanRelationBO.Extra();
                PlanRelationBO.CustomInfo customInfo = new PlanRelationBO.CustomInfo();
                customInfo.setName(context.getName());
                customInfo.setAddress(context.getAddress());
                customInfo.setLat(context.getLat());
                customInfo.setLon(context.getLon());
                customInfo.setCustomType(context.getRelationType().getCode());
                extra.setCustomInfo(customInfo);
                planRelationBO.setExtra(extra);
            }
        }

        PlanRelationPO planRelationPO = StrategyConvert.convertToPlanRelationPO(planRelationBO);
        Boolean saveResult = planRelationRepository.insertPlanRelationPO(planRelationPO);
        if (saveResult) {
            String key = StrategyOfflineKeyBuild.planRelationListKey(planRelationBO.getBusinessScene(), planRelationBO.getBusinessId());
            redisService.hSet(key, String.valueOf(planRelationPO.getId()), JSONObject.toJSONString(StrategyConvert.convertToPlanRelationBO(planRelationPO)));
            postHandle(planRelationPO.getId(), OperateActionEnum.INSERT);
        }
        return saveResult;
    }

    @Override
    public Boolean updatePlanRelation(PlanRelationBO planRelationBO) {
        if (planRelationBO == null || planRelationBO.getId() == null) {
            throw new BizException(BizErrorCodeEnum.PARAM_ERROR);
        }
        PlanRelationBO alreadyPlanRelationBO = queryPlanRelationById(planRelationBO.getId());
        if (alreadyPlanRelationBO == null) {
            log.warn("PlanRelationService#updatePlanRelation 更新计划关联失败，当前数据不存在，无法操作 planRelationBO：【{}】", JSONObject.toJSONString(planRelationBO));
            throw new BizException(BizErrorCodeEnum.DATA_NOT_EXIST);
        }
        //核心校验字段存在变更情况下数据验证
        if (!alreadyPlanRelationBO.getTitle().equals(planRelationBO.getTitle()) || !alreadyPlanRelationBO.getStartTime().equals(planRelationBO.getStartTime())) {
            List<PlanRelationBO> planRelationBOList = queryPlanRelationByBizId(planRelationBO.getBusinessId(), planRelationBO.getBusinessScene());
            if (CollectionUtils.isNotEmpty(planRelationBOList)) {
                if (planRelationBOList.size() > BizConstants.DEFAULT_MAX_PLAN_RELATION_SIZE) {
                    log.error("PlanRelationService#updatePlanRelation 更新计划关联失败，当前业务关联下数据超限，默认不允许操作 planRelationBO：【{}】", JSONObject.toJSONString(planRelationBO));
                    throw new BizException(BizErrorCodeEnum.RELATION_DATA_EXCESSIVE);
                }

                //判断历史数据是否存在某些信息一致
                for (PlanRelationBO item : planRelationBOList) {
                    if (item.getTitle().equals(planRelationBO.getTitle()) && item.getPlanDay().equals(planRelationBO.getPlanDay())) {
                        log.error("PlanRelationService#updatePlanRelation 更新计划关联失败，当前业务关联下标题名称重复，默认不允许操作 planRelationBO：【{}】", JSONObject.toJSONString(planRelationBO));
                        throw new BizException(BizErrorCodeEnum.PARAM_ERROR, "标题名称重复");
                    }
                    if (item.getStartTime().equals(planRelationBO.getStartTime()) && item.getPlanDay().equals(planRelationBO.getPlanDay())) {
                        log.error("PlanRelationService#updatePlanRelation 更新计划关联失败，当前业务关联下开始时间重复，默认不允许操作 planRelationBO：【{}】", JSONObject.toJSONString(planRelationBO));
                        throw new BizException(BizErrorCodeEnum.PARAM_ERROR, "开始时间重复");
                    }
                }
            }
        }
        PlanRelationPO planRelationPO = StrategyConvert.convertToPlanRelationPO(planRelationBO);
        Boolean updateResult = planRelationRepository.updatePlanRelationPOById(planRelationPO);
        if (updateResult) {
            String key = StrategyOfflineKeyBuild.planRelationListKey(planRelationBO.getBusinessScene(), planRelationBO.getBusinessId());
            redisService.hDel(key, String.valueOf(alreadyPlanRelationBO.getId()));
            redisService.hSet(key, String.valueOf(planRelationPO.getId()), JSONObject.toJSONString(StrategyConvert.convertToPlanRelationBO(planRelationPO)));
            postHandle(planRelationPO.getId(), OperateActionEnum.UPDATE);
        }
        return updateResult;
    }

    @Override
    public Boolean removePlanRelationByIdList(Long businessId, PlanRelationSceneEnum businessScene, List<Long> idList) {
        if (CollectionUtils.isEmpty(idList) || businessId == null || businessScene == null) {
            throw new BizException(BizErrorCodeEnum.PARAM_ERROR);
        }
        Boolean removeResult = planRelationRepository.removePlanRelationByIdList(businessId, businessScene.getType(), idList);
        if (removeResult) {
            String key = StrategyOfflineKeyBuild.planRelationListKey(businessScene, businessId);
            for (Long id : idList) {
                redisService.hDel(key, String.valueOf(id));
                postHandle(id, OperateActionEnum.REMOTE);
            }
        }
        return removeResult;
    }

    @Override
    public Boolean removePlanRelationByBizId(Long businessId, PlanRelationSceneEnum businessScene) {
        if (businessId == null || businessScene == null) {
            throw new BizException(BizErrorCodeEnum.PARAM_ERROR);
        }
        List<PlanRelationBO> planRelationBOS = queryPlanRelationByBizId(businessId, businessScene);
        Boolean removeResult = planRelationRepository.removePlanRelationByBizId(businessId, businessScene.getType());
        if (removeResult) {
            String key = StrategyOfflineKeyBuild.planRelationListKey(businessScene, businessId);
            redisService.del(key);
            for (PlanRelationBO planRelationBO : planRelationBOS) {
                postHandle(planRelationBO.getId(), OperateActionEnum.REMOTE);
            }
        }
        return removeResult;
    }

    @Override
    public PlanRelationBO queryPlanRelationById(Long id) {
        if (id == null || id < 0) {
            return null;
        }
        PlanRelationPO planRelationPO = planRelationRepository.queryPlanRelationPOById(id);
        return StrategyConvert.convertToPlanRelationBO(planRelationPO);
    }

    @Override
    public Boolean removePlanRelationById(Long id) {
        if (id == null || id < 0) {
            return false;
        }
        PlanRelationBO planRelationBO = queryPlanRelationById(id);
        if (planRelationBO == null) {
            return true;
        }
        return removePlanRelationByIdList(planRelationBO.getBusinessId(), planRelationBO.getBusinessScene(), Collections.singletonList(id));
    }

    @Override
    public List<PlanRelationBO> queryPlanRelationByBizId(Long businessId, PlanRelationSceneEnum businessScene) {
        if (businessId == null || businessScene == null) {
            return Collections.emptyList();
        }
        String key = StrategyOfflineKeyBuild.planRelationListKey(businessScene, businessId);
        Map<String, String> cachedMap = redisService.hGetAll(key);
        if (MapUtils.isNotEmpty(cachedMap)) {
            return cachedMap.values().stream().map(item -> JSONObject.parseObject(item, PlanRelationBO.class)).collect(Collectors.toList());
        }
        List<PlanRelationPO> planRelationPOList = planRelationRepository.queryPlanRelationListByBizId(businessId, businessScene.getType());
        if (planRelationPOList == null) {
            log.info("PlanRelationService#queryPlanRelationByBizId 根据业务Id查询数据列表完成，数据为空 businessId：【{}】,businessScene:【{}】", businessId, businessScene);
            return Collections.emptyList();
        }
        List<PlanRelationBO> collect = planRelationPOList.stream().map(StrategyConvert::convertToPlanRelationBO).collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(collect)) {
            for (PlanRelationBO item : collect) {
                redisService.hSet(key, String.valueOf(item.getId()), JSONObject.toJSONString(item));
            }
        }
        return collect;
    }

    @Override
    public Map<Integer, List<PlanRelationBO>> queryPlanRelationMapByBizId(Long businessId, PlanRelationSceneEnum businessScene) {
        List<PlanRelationBO> planRelationBOS = queryPlanRelationByBizId(businessId, businessScene);
        if (CollectionUtils.isEmpty(planRelationBOS)) {
            return new HashMap<>();
        }
        Map<Integer, List<PlanRelationBO>> collect = planRelationBOS.stream().collect(Collectors.groupingBy(PlanRelationBO::getPlanDay));
        for (Integer key : collect.keySet()) {
            collect.put(key, collect.get(key).stream().sorted(Comparator.comparing(PlanRelationBO::getStartTime)).collect(Collectors.toList()));
        }
        return collect;
    }

    @Override
    public Boolean insertPlanRelationList(List<PlanRelationBO> planRelationBOList) {
        if (CollectionUtils.isEmpty(planRelationBOList)) {
            throw new BizException(BizErrorCodeEnum.PARAM_ERROR);
        }

        //取第一个作为数据基准
        PlanRelationBO firstPlanRelationBO = planRelationBOList.get(0);
        Long businessId = firstPlanRelationBO.getBusinessId();
        PlanRelationSceneEnum businessScene = firstPlanRelationBO.getBusinessScene();
        for (PlanRelationBO planRelationBO : planRelationBOList) {
            if (planRelationBO.getBusinessId() == null || planRelationBO.getBusinessScene() == null) {
                throw new BizException(BizErrorCodeEnum.PARAM_ERROR, "核心数据为空");
            }
            if (!planRelationBO.getBusinessId().equals(businessId) || !planRelationBO.getBusinessScene().equals(businessScene)) {
                throw new BizException(BizErrorCodeEnum.PARAM_ERROR, "核心数据不一致");
            }
        }

        List<PlanRelationBO> alreadyPlanRelationBOList = queryPlanRelationByBizId(businessId, businessScene);
        if (CollectionUtils.isNotEmpty(alreadyPlanRelationBOList)) {
            int sunCount = planRelationBOList.size() + alreadyPlanRelationBOList.size();
            if (sunCount > BizConstants.DEFAULT_MAX_PLAN_RELATION_SIZE) {
                log.warn("PlanRelationService#insertPlanRelationList 新增计划关联信息失败，当前业务关联下数据超限，默认不允许操作 businessId:[{}],businessScene:[{}],当前数量:[{}]，新增后数量:[{}]", businessId, businessScene, alreadyPlanRelationBOList.size(), sunCount);
                throw new BizException(BizErrorCodeEnum.RELATION_DATA_EXCESSIVE);
            }

            //判断历史数据是否存在某些信息一致
            for (PlanRelationBO item : alreadyPlanRelationBOList) {
                for (PlanRelationBO planRelationBO : planRelationBOList) {
                    if (item.getTitle().equals(planRelationBO.getTitle()) && item.getPlanDay().equals(planRelationBO.getPlanDay())) {
                        planRelationBO.setTitle(item.getTitle() + "_复制");
                    }
                    if (item.getStartTime().equals(planRelationBO.getStartTime()) && item.getPlanDay().equals(planRelationBO.getPlanDay())) {
                        planRelationBO.setStartTime("00:00:00");
                    }
                }
            }
        }
        List<PlanRelationPO> collect = planRelationBOList.stream().map(StrategyConvert::convertToPlanRelationPO).collect(Collectors.toList());
        Boolean updated = planRelationRepository.insertPlanRelationList(collect);
        if (updated) {
            //缓存操作
            if (CollectionUtils.isNotEmpty(planRelationBOList)) {
                String key = StrategyOfflineKeyBuild.planRelationListKey(businessScene, businessId);
                for (PlanRelationBO item : planRelationBOList) {
                    redisService.hSet(key, String.valueOf(item.getId()), JSONObject.toJSONString(item));
                    postHandle(item.getId(), OperateActionEnum.INSERT);
                }
            }
        }
        return updated;
    }

    private void postHandle(Long relationId, OperateActionEnum operateActionEnum) {
        if (OperateActionEnum.REMOTE.equals(operateActionEnum)) {
            OfflineToOnlineRequest request = new OfflineToOnlineRequest();
            request.setBusinessId(relationId);
            request.setUniKey(TodoCommonUtils.buildStriHashKey(relationId, new Date()));
            request.setOperateActionEnum(operateActionEnum);
            request.setStrategyActionEnum(StrategyActionEnum.PLAN_RELATION);
            request.setOriginData(new JSONObject().toJSONString());
            BizResponse<Boolean> bizResponse = strategyPipelineRemoteService.offlineToOnlineAsync(request);
            if (bizResponse == null || !bizResponse.isSuccess() || !bizResponse.getData()) {
                log.error("PlanRelationService#postHandle 平台备忘录发布状态处理失败，数据同步失败 request:{}", JSONObject.toJSONString(request));
            }
        } else {
            PlanRelationPO relationPO = planRelationRepository.queryPlanRelationPOById(relationId);
            if (relationPO == null) {
                log.error("PlanRelationService#postHandle 根据主键更新备忘录状态失败，根据Id查询数据不存在 relationId:{}", relationId);
                return;
            }
            OfflineToOnlineRequest request = new OfflineToOnlineRequest();
            request.setBusinessId(relationId);
            request.setUniKey(TodoCommonUtils.buildStriHashKey(relationId, relationPO.getUpdateTime()));
            request.setOperateActionEnum(operateActionEnum);
            request.setStrategyActionEnum(StrategyActionEnum.PLAN_RELATION);
            request.setOriginData(JSONObject.toJSONString(relationPO));
            BizResponse<Boolean> bizResponse = strategyPipelineRemoteService.offlineToOnlineAsync(request);
            if (bizResponse == null || !bizResponse.isSuccess() || !bizResponse.getData()) {
                log.error("PlanRelationService#postHandle 平台备忘录发布状态处理失败，数据同步失败 request:{}", JSONObject.toJSONString(request));
            }
        }
    }
}
